﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace BlockEngine.World.Renderer
{
    class Cube
    {
        private Engine.Internal.Vector3F[] cubeVertexData = null;
        private Engine.Internal.Vector3F[] cubeNormalData = null;
        private int[] colorData = null;
        private uint[] indicesVboData = null;

        int colorBufferID;
        int normalBufferID;
        int vertexBufferID;
        int indiciesBufferID;

        int elementCount = 0;

        private void CreateMesh(Engine.Internal.Point3D Position)
        {
            cubeVertexData = new Engine.Internal.Vector3F[] {
                new Engine.Internal.Vector3F(Position.X - 0.5f, Position.Y - 0.5f, Position.Z + 0.5f),
                new Engine.Internal.Vector3F(Position.X + 0.5f, Position.Y - 0.5f, Position.Z + 0.5f),
                new Engine.Internal.Vector3F(Position.X + 0.5f, Position.Y + 0.5f, Position.Z + 0.5f),
                new Engine.Internal.Vector3F(Position.X - 0.5f, Position.Y + 0.5f, Position.Z + 0.5f),
                new Engine.Internal.Vector3F(Position.X - 0.5f, Position.Y - 0.5f, Position.Z - 0.5f),
                new Engine.Internal.Vector3F(Position.X + 0.5f, Position.Y - 0.5f, Position.Z - 0.5f),
                new Engine.Internal.Vector3F(Position.X + 0.5f, Position.Y + 0.5f, Position.Z - 0.5f),
                new Engine.Internal.Vector3F(Position.X - 0.5f, Position.Y + 0.5f, Position.Z - 0.5f),
            };
            cubeNormalData = cubeVertexData;
            colorData = new int[] {
                Engine.API.Maths.ColorToRgba32(Color.DarkCyan),
                Engine.API.Maths.ColorToRgba32(Color.DarkCyan),
                Engine.API.Maths.ColorToRgba32(Color.Cyan),
                Engine.API.Maths.ColorToRgba32(Color.Cyan),
                Engine.API.Maths.ColorToRgba32(Color.DarkCyan),
                Engine.API.Maths.ColorToRgba32(Color.DarkCyan),
                Engine.API.Maths.ColorToRgba32(Color.Cyan),
                Engine.API.Maths.ColorToRgba32(Color.Cyan),
            };
            indicesVboData = new uint[] { 
				0, 1, 2, 2, 3, 0, 
				3, 2, 6, 6, 7, 3, 
				7, 6, 5, 5, 4, 7, 
				4, 0, 3, 3, 7, 4, 
				0, 1, 5, 5, 4, 0,
				1, 5, 6, 6, 2, 1 
			};
        }
        /// <summary>
        /// Renders a block 
        /// </summary>
        /// <param name="Position">The position to render the block</param>
        /// <param name="ID">The block ID</param>
        public void Render(Engine.Internal.Point3D Position, short ID, bool JustRender)
        {
            if (!JustRender)
                CreateMesh(Position);

            int bufferSize;

            if (colorData != null)
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out colorBufferID);

                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);

                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(colorData.Length * sizeof(int)), colorData, BufferUsageHint.StaticDraw);

                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);

                if (colorData.Length * sizeof(int) != bufferSize)
                    throw new ApplicationException("Vertex array not uploaded correctly");

                // Clear the buffer Binding
                //GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }

            if (cubeNormalData != null)
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out normalBufferID);

                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, normalBufferID);

                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(cubeNormalData.Length * Engine.Internal.Vector3F.ByteSize), cubeNormalData, BufferUsageHint.StaticDraw);

                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (cubeNormalData.Length * Engine.Internal.Vector3F.ByteSize != bufferSize)
                    throw new ApplicationException("Normal array not uploaded correctly");

                // Clear the buffer Binding
                //GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }

            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out vertexBufferID);

                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);

                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(cubeVertexData.Length * Engine.Internal.Vector3F.ByteSize), cubeVertexData, BufferUsageHint.DynamicDraw);

                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (cubeVertexData.Length * Engine.Internal.Vector3F.ByteSize != bufferSize)
                    throw new ApplicationException("Vertex array not uploaded correctly");

                // Clear the buffer Binding
                //GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }

            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out indiciesBufferID);

                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, indiciesBufferID);

                // Send data to buffer
                GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indicesVboData.Length * sizeof(int)), indicesVboData, BufferUsageHint.StaticDraw);

                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (indicesVboData.Length * sizeof(int) != bufferSize)
                    throw new ApplicationException("Element array not uploaded correctly");

                // Clear the buffer Binding
                //GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
            }

            elementCount = indicesVboData.Length;

            if (vertexBufferID == 0)
                return;
            if (indiciesBufferID == 0)
                return;

            // Color Array Buffer (Colors not used when lighting is enabled)
            if (colorBufferID != 0)
            {
                // Bind to the Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);

                // Set the Pointer to the current bound array describing how the data ia stored
                GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);

                // Enable the client state so it will use this array buffer pointer
                GL.EnableClientState(ArrayCap.ColorArray);
            }

            // Vertex Array Buffer
            {
                // Bind to the Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);

                // Set the Pointer to the current bound array describing how the data ia stored
                GL.VertexPointer(3, VertexPointerType.Float, Engine.Internal.Vector3F.ByteSize, IntPtr.Zero);

                // Enable the client state so it will use this array buffer pointer
                GL.EnableClientState(ArrayCap.VertexArray);
            }

            // Element Array Buffer
            {
                // Bind to the Array Buffer ID
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, indiciesBufferID);

                // Draw the elements in the element array buffer
                // Draws up items in the Color, Vertex, TexCoordinate, and Normal Buffers using indices in the ElementArrayBuffer
                GL.DrawElements(BeginMode.Triangles, elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero);
            }
        }
    }
}
